using System;
using System.Threading;
using System.Collections;
using Sloppycode.net;
using TinyServerLib;
using System.IO;
using System.Configuration;

using TinyLogger;

namespace RssAggregator
{
	/// <summary>
	/// Aggregator
	/// Version: 0.1   draft
	/// Copyright 2004 (c) Stephan Meyn
	/// Role: aggregate feeds
	/// Responsibilities:
	/// <list type="bullet">
	/// <item>Can read app config for RSS urls</item>
	/// <item>Can download RSS items on regular basis</item>
	/// <item>Can provide RSS Summary page via web server</item>
	/// </list>
	/// 
	/// 
	/// </summary>
	public class Aggregator
	{
		#region private fields
		private RssFeedStore store;
		private int milliSecondsToSleep = 10000;
		private AggServer server;
		private Thread checkThread;
		#endregion

		#region public Attributes
		/// <summary>
		/// sleep interval between each check to see if feeds need updating
		/// </summary>
		public int MilliSecondsToSleep { get { return milliSecondsToSleep;} set { milliSecondsToSleep = value;}}

		/// <summary>
		/// the directory where RssFeeds can be stored
		/// </summary>
		public string RssStorePath;
		#endregion

		#region construction
		public Aggregator()
		{
			store = new RssFeedStore();
	
			server = new AggServer(store);
		}

		#endregion

		#region public methods
		/// <summary>
		/// regularly poll the feeds and keep them upto date
		/// this method should be called in a separate thread.
		/// </summary>
		public void Run()
		{
			configure(server);
			server.Run();
			checkThread = new Thread(new ThreadStart(checkFeedsPeriodically));
			checkThread.IsBackground = true;
			checkThread.Name = "check thread";
			checkThread.Start();
		}

		public void Stop()
		{
			if (checkThread != null)
				checkThread.Abort();
			if (server != null)
				server.Stop();
		}
		/// <summary>
		/// should run in its own thread
		/// </summary>
		private void checkFeedsPeriodically()
		{		
			for(;;)
			{
				checkFeeds();
				Thread.Sleep(MilliSecondsToSleep);
			}
		}
		/// <summary>
		/// add another RSS URL
		/// </summary>
		/// <param name="url"></param>
		public void AddRssUrl(string url)
		{
			store.AddFeed(url);
		}
		#endregion

		#region private methods

		/// <summary>
		/// do the rounds
		/// </summary>
		private void checkFeeds()
		{
			ArrayList forUpdate = new ArrayList();
			foreach (string url in store.Urls)
			{
				if (feedNeedsUpdating(url))
					forUpdate.Add(url);
			}
			foreach(string url in forUpdate)
			{
				updateFeed(url);
			}
		}
		/// <summary>
		/// determine if a feed needs updating
		/// </summary>
		/// <param name="url"></param>
		/// <returns></returns>
		private bool feedNeedsUpdating(string url)
		{
			RssFeed feed = store[url];
			if (feed == null)
				return true;
			
			TimeSpan age = DateTime.Now.Subtract(feed.LastDownloaded);
			//check time to live
			if (feed.Ttl.Length > 0)
			{
				try
				{
					int ttlMins = int.Parse(feed.Ttl);
					return  ((age.Hours > 0) && (age.Minutes > ttlMins));
				}
				catch (Exception){}
			}
			return (age.Hours > 0);
		}
		/// <summary>
		/// update the store
		/// </summary>
		/// <param name="url"></param>
		private void updateFeed(string url)
		{
			System.Diagnostics.Debug.WriteLine("Update RSSFeed", url);

			Console.WriteLine("{0}:Updating Feed: {1}",DateTime.Now.TimeOfDay, url);
			RssFeed feed = RssReader.GetFeed(url);
			// lock access to the store
			lock(this)
			{
				store[url] = feed;
			}
		}

		#endregion

		#region server
		/// <summary>
		/// configure the web server
		/// </summary>
		/// <param name="server"></param>
		private void configure(AggServer server)
		{
            server.ServerLogger.AddLoggerSink(new ConsoleLogger());
            server.ServerLogger.AddLoggerSink(new FileLogger("RssAggregator.log"));
            server.ServerLogger.SetLevel(LogLevel.Informational);

            server.Templates = Properties.Settings.Default.TemplatePath; 
            server.DefaultPage = Properties.Settings.Default.DefaultPage;
            server.WebRootPath = Properties.Settings.Default.WebRoot; 
            server.Port = Properties.Settings.Default.Port;
            string logLevel = Properties.Settings.Default.LogLevel;
            switch (logLevel)
            {
                case "ALL": server.ServerLogger.SetLevel(LogLevel.Informational);
                    break;
                case "WARNING": server.ServerLogger.SetLevel(LogLevel.Warning);
                    break;
                case "ERROR": server.ServerLogger.SetLevel(LogLevel.Error);
                    break;
                case "NONE": server.ServerLogger.SetLevel(LogLevel.Fatal);
                    break;
			}
		}
		#endregion
	}
}
